﻿using System;
using NJabber.Client.StanzaFactories;
using NJabber.Stream;
using NJabber.Stream.Elements;

namespace NJabber.Client
{
    public class RosterSynchronizer : IRosterSynchronizer, IStanzaHandler<IqStanza>
    {
        private IXmppConnection connection;
        private IRosterStanzaFactory factory;
        private IRosterUpdater updater;
        private bool connectionInitialized;
        private bool rosterRequested;
        private bool rosterReceived;

        public RosterSynchronizer(IXmppConnection connection, IRosterStanzaFactory factory, IRosterUpdater updater)
        {
            if (connection == null) throw new ArgumentNullException("connection");
            if (factory == null) throw new ArgumentNullException("factory");
            if (updater == null) throw new ArgumentNullException("updater");

            this.connection = connection;
            this.factory = factory;
            this.updater = updater;
        }

        protected ConnectionState ConnectionState
        {
            get { return connection.State; }
        }

        protected IStanzaStream Stream
        {
            get {    return connection.Stream; }
        }

        public void Sync(RosterChanges changes)
        {
            if (changes == null) throw new ArgumentNullException("changes");
            if (!IsConnectionReady()) return;
            if (ConnectionState == ConnectionState.Closed) return;

            if (rosterReceived) updater.Update(changes);
            else SendRosterRequest();
        }

        private bool IsConnectionReady()
        {
            if (connectionInitialized && connection != null) return true;
            if (connection != null)
            {
                InitializeConnection();
                return true;
            }
            return false;
        }

        private void InitializeConnection()
        {
            connection.Closed += (sender, args) =>
                {
                    rosterRequested = false;
                    rosterReceived = false;
                };
            connectionInitialized = true;
        }

        private void SendRosterRequest()
        {
            if (!rosterRequested)
            {
                Stanza rosterRequest = CreateRosterRequest();
                Stream.Write(rosterRequest);
                rosterRequested = true;
            }
        }

        private Stanza CreateRosterRequest()
        {
            return factory.CreateRosterRequest(connection.Jid);
        }

        public bool ProcessStanza(XmppClientRuntime runtime, IqStanza stanza)
        {
            if (IsConnectionReady() && stanza.IsRosterResult()) rosterReceived = true;
            return true;
        }
    }
}
